import GameConfig from "./viewer/GameConfig";
import { Square } from "./Square";
import { SquareGroup } from "./SquareGroup";
import { createTeris } from "./Teris";
import { TerisRules } from "./TerisRules";
import { GameStatus, GameViewer, MoveDirection } from "./types";

export class Game {
  private _gameStatus: GameStatus = GameStatus.init;
  public get gameStatus() {
    return this._gameStatus;
  }

  private _curTeris?: SquareGroup; // 当前用户当前操作的方块
  private _nextTeris: SquareGroup; // 当前用户下一个显示的方块
  private timer?: number; // 自由下落的定时器
  private _duration: number = GameConfig.level[0].duration; // 定时器间隔时间 s

  // 保存已经存在的小方块
  private _exits: Square[] = [];

  private _score: number = 0;
  public get score() {
    return this._score;
  }

  private set score(val) {
    this._score = val;
    this._viewer.showScore(val);
    const lev = GameConfig.level.filter(v => v.score <= val);
    const last = lev[lev.length - 1];
    if (last.duration === this._duration) return;
    this._duration = last.duration;
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = undefined;
      this.autoDrop();
    }
  }

  constructor(private _viewer: GameViewer) {
    this._nextTeris = createTeris({ x: 0, y: 0 });
    this.createNext();

    this._viewer.init(this);
  }

  private init() {
    this._exits.forEach(sq => {
      sq.viewer?.remove();
    });
    this._exits = [];
    this._gameStatus = GameStatus.init;
    this._curTeris = undefined;
    this.timer = undefined;
    this._duration = GameConfig.level[0].duration;
    this.createNext();
  }

  /**
   * 添加下一个方块
   */
  private createNext() {
    this._nextTeris = createTeris({ x: 0, y: 0 });
    this.resetCenterPoint(GameConfig.nextSize.width, this._nextTeris);
    this._viewer.showNext(this._nextTeris);
  }

  /**
   * 游戏开始
   */
  start() {
    if (this._gameStatus === GameStatus.playing) return;
    if (this._gameStatus === GameStatus.over) {
      // 重新开始
      this.init();
      return;
    }

    this._gameStatus = GameStatus.playing;
    this._viewer.onStart();

    if (!this._curTeris) {
      // 切换方块
      this.switchTeris();
    }

    // 开始自由降落
    this.autoDrop();
  }

  /**
   * 游戏暂停
   */
  pause() {
    if (this._gameStatus === GameStatus.playing) {
      this._gameStatus = GameStatus.pause;
      clearInterval(this.timer);
      this.timer = undefined;
      this._viewer.onPause();
    }
  }

  /**
   * 切换方块：当前操作的方块永远是下一个显示的方块，
   * 下一个方块随机产生
   */
  private switchTeris() {
    // 把下一个方块 切换给 当前方块
    this._curTeris = this._nextTeris;
    this.resetCenterPoint(GameConfig.panelSize.width, this._curTeris);
    // 下一个方块切换到当前方块时，下一个方块不需要显示
    this._nextTeris.squares.forEach(sq => {
      sq.viewer?.remove();
    });
    // 当前方块，可能已经发生重叠
    if (!TerisRules.canIMove(this._curTeris.shape, this._curTeris.centerPoint, this._exits)) {
      // 游戏结束
      this._gameStatus = GameStatus.over;
      clearInterval(this.timer);
      this.timer = undefined;
      this._viewer.onOver();
      return;
    }

    this.createNext();
    this._viewer.switch(this._curTeris);
  }

  /**
   * 方块自由下移
   */
  private autoDrop() {
    if (this.timer || this._gameStatus !== GameStatus.playing) return;

    this.timer = setInterval(() => {
      if (!TerisRules.move(this._curTeris!, MoveDirection.down, this._exits)) {
        // 触底了
        this.hideBottom();
      }
    }, this._duration);
  }

  /**
   * 设置中心点坐标，让方块处于面板的中上方
   * @param teris
   */
  private resetCenterPoint(width: number, teris: SquareGroup) {
    const x = Math.ceil(width / 2) - 1;
    const y = 0;
    teris.centerPoint = { x, y };
    while (teris.squares.some(it => it.point.y < 0)) {
      teris.centerPoint = {
        x: teris.centerPoint.x,
        y: teris.centerPoint.y + 1
      };
    }
  }

  /**
   * 触底操作
   * 1. 保存已存在数据
   * 2. 消除方块
   */
  private hideBottom() {
    // 保存已落下的小方块
    this._exits = this._exits.concat(...this._curTeris!.squares);
    // 消除方块操作
    const nums = TerisRules.deleteSquares(this._exits);
    // 处理积分
    this.score += this.getCore(nums);
    this.switchTeris();
  }

  /**
   * 根据消除的行数计算积分
   * @param lines
   * @returns
   */
  private getCore(lines: number): number {
    let core = 0;
    switch (lines) {
      case 0:
        break;
      case 1:
        core = 10;
        break;
      case 2:
        core = 20;
        break;
      case 3:
        core = 40;
        break;
      case 4:
        core = 60;
        break;
      default:
        core = 80;
        break;
    }

    return core;
  }

  controlLeft() {
    if (this._curTeris && this._gameStatus === GameStatus.playing) {
      TerisRules.move(this._curTeris, MoveDirection.left, this._exits);
    }
  }

  controlRight() {
    if (this._curTeris && this._gameStatus === GameStatus.playing) {
      TerisRules.move(this._curTeris, MoveDirection.right, this._exits);
    }
  }

  controlDown() {
    if (this._curTeris && this._gameStatus === GameStatus.playing) {
      TerisRules.moveDirectly(this._curTeris, MoveDirection.down, this._exits);
      // 触底了
      this.hideBottom();
    }
  }

  controlRotate() {
    if (this._curTeris && this._gameStatus === GameStatus.playing) {
      TerisRules.rotate(this._curTeris, this._exits);
    }
  }
}
